package znet

import (
	"errors"
	"fmt"
	"gitee.com/MrDaiM/zinx/utils"
	"gitee.com/MrDaiM/zinx/ziface"
	"io"
	"net"
	"sync"

)

type Connection struct {

	// 当前Conn属于哪个Server
	TcpServer ziface.IServer // 当前conn属于哪个server， 在conn初始化的时候添加即可

	// 当前连接的socket TCP 套接字
	Conn *net.TCPConn
	// 当前连接的ID 也可以称作为SessionID， ID全局唯一
	ConnID uint32
	// 当前连接的状态
	isClosed bool

	// 该链接的处理方法router
	Router ziface.IRouter

	// 消息管理MsgId 和 对应处理方法的消息管理模块
	MsgHandler ziface.IMsgHandle

	// 该链接的处理方法api
	// handleAPI ziface.HandFunc

	// 告知该链接已经退出/停止的channel(有Reader 告知 Writer 退出的信号)
	ExitBuffChan chan bool
	// 无缓冲管道，用于读，写两个 goroutine 之间的消息通信
	msgChan chan []byte
	// 有缓冲管道， 用于读， 写 两个 goroutine之间的消息通信
	msgBuffChan chan []byte

	// ============
	// 链接属性
	property map[string]interface{}
	// 保护链接属性修改的锁
	propertyLock sync.RWMutex
	// ============

}

// NewConnection 创建一个构造连接的方法
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer:    server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		MsgHandler:   msgHandler,
		ExitBuffChan: make(chan bool, 1), // 带有缓冲的管道，起到阻塞的作用方式服务端被停掉
		msgChan:      make(chan []byte),
		msgBuffChan:  make(chan []byte, utils.GlobalObject.MaxMsgChanLen),
		property: make(map[string]interface{}),
	}

	// 将新创建的Conn提那家到链接管理器中
	c.TcpServer.GetConnMgr().Add(c) // 将当前新创建的连接提那家到ConnManager中

	return c
}

// StartReader 处理conn读数据的goroutine
func (c *Connection) StartReader() {

	fmt.Println("[Reader Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn reader exit]")
	defer c.Stop()

	for {

		// 创建拆包解包的对象
		dp := NewDataPack()

		// 读取客户端的Msg Head
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error ", err)
			c.ExitBuffChan <- true
			break
		}

		//// 读取我们最大的数据到buf中
		//buf := make([]byte, 512)
		//_, err := c.Conn.Read(buf)
		//if err != nil {
		//	fmt.Println("recv buf err ", err)
		//	c.ExitBuffChan <- true
		//	continue
		//}

		// 拆包，得到msgId 和 dataLen 放在msg中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error:", err)
			c.ExitBuffChan <- true
			break
		}

		// 根据dataLen 读取 data 放在msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data err ", err)
				c.ExitBuffChan <- true
				break
			}
		}

		// 到此消息拆包(二进制包)封装成Message对象完成
		// 设置赋值
		msg.SetData(data)

		// 得到当前客户端请求的Request数据
		req := Request{
			conn: c,
			msg:  msg, // 将之前的 buf 改成 msg
		}

		// 提供异步注册
		// 从路由Routers 中找到绑定Conn对应的Handle
		//go func(request ziface.IRequest) {
		//	// 执行注册的理由方法
		//	c.Router.PreHandle(request)
		//	c.Router.Handle(request)
		//	c.Router.PostHandle(request)
		//}(&req)

		// 判断是否配置工作池参数
		if utils.GlobalObject.WorkerPoolSize > 0 {
			// 已经启动工作池机制，将消息交给Worker处理
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			// 从绑定好的消息和对应的处理方法中执行对应的Handle方法
			go c.MsgHandler.DoMsgHandler(&req)
		}

		// 调用当前连接业务（在这里只想的是当前conn绑定的handle方法）
		//if err := c.handleAPI(c.Conn, buf, cnt); err != nil {
		//	fmt.Println("connId ", c.ConnID, " handle is err")
		//	c.ExitBuffChan <- true
		//	return
		//}

	}

}

/*
	写消息Goroutine， 用户数据发送给客户端
*/
func (c *Connection) StartWriter() {

	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Write exit]")

	// 阻塞等待，StartReader读写入的数据
	for {
		select {
		case data := <-c.msgChan:
			// 数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data err: ", err, " Conn Writer exit")
				return
			}
		case data, ok := <- c. msgBuffChan:
			if ok {
				// 有数据要写给客户端
				if _, err := c.Conn.Write(data); err  != nil {
					fmt.Println("Send buff Data error: ", err , " Conn Writer exit")
					return
				}
			} else {
				break
				fmt.Println("msgBuffer is Closed")
			}

		case <-c.ExitBuffChan:
			// 此时Reader已退出， Writer 也要退出
			return
		}
	}

}

// Start 启动连接，让当前连接开始工作
func (c *Connection) Start() {

	fmt.Println("Conn Start() ...ConnID = ", c.ConnID)

	// 开启该链接读取到客户端数据之后的请求业务
	go c.StartReader()

	// 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter()


	// ================
	// 按照用户传递进来的创建链时需要处理的业务员，执行钩子方法
	c.TcpServer.CallOnConnStart(c)
	// ===============


	for {
		select {
		case <-c.ExitBuffChan:
			// 得到退出消息，不在阻塞
			return
		}

	}

}

// Stop 停止连接，结束当前连接状态
func (c *Connection) Stop() {
	fmt.Println("Conn Start() ...ConnID = ", c.ConnID)
	// 1. 如果当前来内已经关闭
	if c.isClosed == true {
		return
	}
	// 修改状态
	c.isClosed = true

	// =================
	// 如果用户注册了该链接的关闭回调业务，那么在此刻显示调用
	c.TcpServer.CallOnConnStop(c)
	// =================


	// 关闭Socket
	c.Conn.Close()



	// 通知缓冲队列读数据的业务，该链接已关闭
	// 告知Writer关闭
	c.ExitBuffChan <- true

	// Connection Stop() 如果用户注册了该链关闭回调业务，那么在此刻应该显示调用
	// 将链接从管理器中删除
	c.TcpServer.GetConnMgr().Remove(c) // 删除conn从ConnManager中

	// 关闭噶连接的全部管道
	close(c.ExitBuffChan)
	// 关闭消息通道
	close(c.msgChan)

}

// GetTCPConnection 从当前连接获取原始的socket TCPConn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// GetConnId 获取当前连接ID
func (c *Connection) GetConnId() uint32 {

	return c.ConnID
}

// RemoteAddr 获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// SendMsg 直接将Message数据发送给远程的TCP客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {

	// 如果当前服务器已经停止，拒绝发送消息
	if c.isClosed == true {
		return errors.New(" Connection closed when send msg ")
	}
	// 将data封包，并且发送出去
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return errors.New("Pack error msg ")
	}

	// 回写客户端
	//if _, err := c.Conn.Write(msg); err != nil {
	//	fmt.Println("Write msg id ", msgId, "error ", err)
	//	c.ExitBuffChan <- true
	//	return errors.New("conn Write error")
	//}
	c.msgChan <- msg

	return nil

}

func (c *Connection) SendBuffMsg(msgId uint32, data []byte) error {

	if c.isClosed == true {
		return errors.New("Connection closed when send buff msg ")
	}
	// 将data封装包，并且发送

	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg isd = ", msg)
		return errors.New("Pack error msg ")
	}

	// 回写给客户端
	c.msgBuffChan <- msg

	return nil
}


// SetProperty 设置链接属性
func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	c.property[key] = value

}
// GetProperty 获取链接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
    if value , ok := c.property[key]; ok {
    	return value, nil
	}
	return  nil, errors.New("no property found")


}
// RemoveProperty 移除链接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}